package edu.xhu.video.service.impl;

import edu.xhu.api.id.IdClient;
import edu.xhu.model.common.constants.CheckConstants;
import edu.xhu.model.common.dtos.ResponseResult;
import edu.xhu.model.video.dtos.BiliVideoUploadDto;
import edu.xhu.model.video.enums.CreateType;
import edu.xhu.model.video.enums.VideoAuth;
import edu.xhu.model.video.enums.VideoStatus;
import edu.xhu.model.video.enums.VideoType;
import edu.xhu.model.video.pojos.BiliVideo;
import edu.xhu.model.video.pojos.BiliVideoData;
import edu.xhu.utils.thread.BiliUserThreadLocalUtil;
import edu.xhu.video.mapper.BiliVideoDataMapper;
import edu.xhu.video.mapper.BiliVideoMapper;
import edu.xhu.video.service.BiliUploadService;
import edu.xhu.video.service.VideoCheckService;
import edu.xhu.video.service.VideoCommentService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import edu.xhu.oss.utils.BiliOssUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 视频评论服务类
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class BiliUploadServiceImpl implements BiliUploadService {
    private final BiliOssUtils biliOssUtils;

    private final StringRedisTemplate redisTemplate;

    private final BiliVideoMapper biliVideoMapper;

    private final BiliVideoDataMapper biliVideoDataMapper;

    private final Executor checkCommitExecutor;

    private final VideoCheckService videoCheckService;

    private final ReentrantLock lock = new ReentrantLock();

    @Resource
    private IdClient idClient;

    /**
     * 获取视频下一个还没上传的分片序号
     *
     * @param hash 视频的hash值
     * @return CustomResponse对象
     */
    @Override
    public ResponseResult<?> askCurrentChunk(String hash) {
        return ResponseResult.okResult(biliOssUtils.getChunkCount(hash));
    }

    /**
     * 上传单个视频分片，当前上传到阿里云对象存储
     *
     * @param chunk 分片文件
     * @param hash  视频的hash值
     * @param index 当前分片的序号
     * @return CustomResponse对象
     * @throws IOException
     */
    @Override
    public ResponseResult<?> uploadChunk(MultipartFile chunk, String hash, Integer index) {
        try {
            Boolean b = biliOssUtils.uploadChunk(chunk, hash, index);
            if (b) {
                return ResponseResult.okResult(200, "上传成功");
            }
            return ResponseResult.errorResult(500, "上传失败");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 取消上传并且删除该视频的分片文件
     *
     * @param hash 视频的hash值
     * @return CustomResponse对象
     */
    @Override
    public ResponseResult<?> cancelUpload(String hash) {
        try {
            biliOssUtils.removeTemporaryChunks(hash);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return ResponseResult.okResult(200, "取消上传成功");
    }

    @Override
    public ResponseResult<?> addVideo(BiliVideoUploadDto biliVideoUploadDto) {
        if (biliVideoUploadDto.getTitle().trim().isEmpty()) {
            return ResponseResult.errorResult(500, "标题不能为空");
        }
        if (biliVideoUploadDto.getTitle().length() > 80) {
            return ResponseResult.errorResult(500, "标题不能超过80字");
        }
        if (biliVideoUploadDto.getDescr().length() > 2000) {
            return ResponseResult.errorResult(500, "简介太长啦");
        }
        try {
            Long userId = BiliUserThreadLocalUtil.getUser().getUserId();
            String md5 = biliVideoUploadDto.getHash();
            String coverUrl = biliOssUtils.uploadImageFile(md5, biliVideoUploadDto.getCover());
            String videoUrl = biliOssUtils.composeChunks(md5);
            biliOssUtils.removeTemporaryChunks(md5);


            String coverPathUrl = "/bili-image/" + coverUrl.split("/")[3];
            String videoPathUrl = "/bili-video/" + videoUrl.split("/")[3];
            Long videoId = Long.parseLong(idClient.getSnowflakeDistributedId().getData().toString());
            lock.lock();
            BiliVideo biliVideo = new BiliVideo()
                    .setVideoId(videoId)
                    .setUserId(userId)
                    .setTitle(biliVideoUploadDto.getTitle())
                    .setCreateType(biliVideoUploadDto.getType() == 0 ? CreateType.SELF_MADE.getCode() : CreateType.REPRODUCED.getCode())
                    .setAuth(biliVideoUploadDto.getAuth() == 0 ? VideoAuth.NO_STATEMENT.getCode() : VideoAuth.WITHOUT_PERMISSION.getCode())
                    .setVideoType(VideoType.COMMON.getCode())
                    .setState(VideoStatus.CHECK.getCode())
                    .setSubChannelId(1L)
                    .setTags(biliVideoUploadDto.getTags())
                    .setDescription(biliVideoUploadDto.getDescr())
                    .setCoverUrl(coverUrl)
                    .setVideoUrl(videoUrl)
                    .setCreateTime(LocalDateTime.now())
                    .setDeleteTime(null)
                    .setDuration(biliVideoUploadDto.getDuration().longValue())
                    .setIsCompilation("0")
                    .setIsTop("0")
                    .setUpdateEpisode(0)
                    .setEpisodeTime("周一")
                    .setCoverPathUrl(coverPathUrl)
                    .setVideoPathUrl(videoPathUrl);
            BiliVideoData biliVideoData=new BiliVideoData()
                    .setVideoId(videoId)
                    .setPlay(0L)
                    .setDanmu(0L)
                    .setLikes(0L)
                    .setCoin(0L)
                    .setCollect(0L)
                    .setShares(0L)
                    .setComments(0L)
                    .setUpdateTime(LocalDateTime.now());
            //TODO,插入db,video判空,保证video是正常video
            biliVideoMapper.insert(biliVideo);
            biliVideoDataMapper.insert(biliVideoData);
            lock.unlock();
            //TODO,异步派个子线程去提交审核
            //插到db后,缓存个id到redis,永不过期,用kkv,k,id,id
            redisTemplate.opsForHash().put(CheckConstants.CHECK_HASH, biliVideo.getVideoId().toString(), biliVideo.getVideoId().toString());
            CompletableFuture.runAsync(() -> videoCheckService.videoCheckCommit(biliVideo), checkCommitExecutor);
            return ResponseResult.okResult(200, "上传成功");
        } catch (IOException e) {
            return ResponseResult.okResult(500, "上传失败");
        }
    }
}
